﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm;
using YJKGridTopologyCal.Geometries.Utilities;
using YJKGridTopologyCal.GeometriesGraph;
using YJKGridTopologyCal.GeometriesGraph.Index;

namespace YJKGridTopologyCal.Operation
{
	public class IsSimpleOp
	{
		[Obsolete("Use IsSimpleOp(IGeometry geom)")]
		public IsSimpleOp()
		{
		}

		public IsSimpleOp(IGeometry geom)
		{
			this._inputGeom = geom;
		}

		public IsSimpleOp(IGeometry geom, IBoundaryNodeRule boundaryNodeRule)
		{
			this._inputGeom = geom;
			this._isClosedEndpointsInInterior = !boundaryNodeRule.IsInBoundary(2);
		}

		public bool IsSimple()
		{
			this._nonSimpleLocation = null;
			return this.ComputeSimple(this._inputGeom);
		}

		private bool ComputeSimple(IGeometry geom)
		{
			this._nonSimpleLocation = null;
			if (geom.IsEmpty)
			{
				return true;
			}
			if (geom is ILineString)
			{
				return this.IsSimpleLinearGeometry(geom);
			}
			if (geom is IMultiLineString)
			{
				return this.IsSimpleLinearGeometry(geom);
			}
			if (geom is IMultiPoint)
			{
				return this.IsSimpleMultiPoint((IMultiPoint)geom);
			}
			if (geom is IPolygonal)
			{
				return this.IsSimplePolygonal(geom);
			}
			return !(geom is IGeometryCollection) || this.IsSimpleGeometryCollection(geom);
		}

		public Coordinate NonSimpleLocation
		{
			get
			{
				return this._nonSimpleLocation;
			}
		}

		[Obsolete("Use IsSimple()")]
		public bool IsSimple(ILineString geom)
		{
			return this.IsSimpleLinearGeometry(geom);
		}

		[Obsolete("Use IsSimple()")]
		public bool IsSimple(IMultiLineString geom)
		{
			return this.IsSimpleLinearGeometry(geom);
		}

		[Obsolete("Use IsSimple()")]
		public bool IsSimple(IMultiPoint mp)
		{
			return this.IsSimpleMultiPoint(mp);
		}

		private bool IsSimpleMultiPoint(IMultiPoint mp)
		{
			if (mp.IsEmpty)
			{
				return true;
			}
			HashSet<Coordinate> hashSet = new HashSet<Coordinate>();
			for (int i = 0; i < mp.NumGeometries; i++)
			{
				Coordinate coordinate = ((IPoint)mp.GetGeometryN(i)).Coordinate;
				if (hashSet.Contains(coordinate))
				{
					this._nonSimpleLocation = coordinate;
					return false;
				}
				hashSet.Add(coordinate);
			}
			return true;
		}

		private bool IsSimplePolygonal(IGeometry geom)
		{
			foreach (IGeometry geometry in LinearComponentExtracter.GetLines(geom))
			{
				ILinearRing geom2 = (ILinearRing)geometry;
				if (!this.IsSimpleLinearGeometry(geom2))
				{
					return false;
				}
			}
			return true;
		}

		private bool IsSimpleGeometryCollection(IGeometry geom)
		{
			for (int i = 0; i < geom.NumGeometries; i++)
			{
				IGeometry geometryN = geom.GetGeometryN(i);
				if (!this.ComputeSimple(geometryN))
				{
					return false;
				}
			}
			return true;
		}

		private bool IsSimpleLinearGeometry(IGeometry geom)
		{
			if (geom.IsEmpty)
			{
				return true;
			}
			GeometryGraph geometryGraph = new GeometryGraph(0, geom);
			LineIntersector li = new RobustLineIntersector();
			SegmentIntersector segmentIntersector = geometryGraph.ComputeSelfNodes(li, true);
			if (!segmentIntersector.HasIntersection)
			{
				return true;
			}
			if (segmentIntersector.HasProperIntersection)
			{
				this._nonSimpleLocation = segmentIntersector.ProperIntersectionPoint;
				return false;
			}
			return !this.HasNonEndpointIntersection(geometryGraph) && (!this._isClosedEndpointsInInterior || !this.HasClosedEndpointIntersection(geometryGraph));
		}

		private bool HasNonEndpointIntersection(GeometryGraph graph)
		{
			foreach (Edge edge in graph.Edges)
			{
				int maximumSegmentIndex = edge.MaximumSegmentIndex;
				foreach (EdgeIntersection edgeIntersection in edge.EdgeIntersectionList)
				{
					if (!edgeIntersection.IsEndPoint(maximumSegmentIndex))
					{
						this._nonSimpleLocation = edgeIntersection.Coordinate;
						return true;
					}
				}
			}
			return false;
		}

		private bool HasClosedEndpointIntersection(GeometryGraph graph)
		{
			IDictionary<Coordinate, IsSimpleOp.EndpointInfo> dictionary = new SortedDictionary<Coordinate, IsSimpleOp.EndpointInfo>();
			foreach (Edge edge in graph.Edges)
			{
				bool isClosed = edge.IsClosed;
				Coordinate coordinate = edge.GetCoordinate(0);
				IsSimpleOp.AddEndpoint(dictionary, coordinate, isClosed);
				Coordinate coordinate2 = edge.GetCoordinate(edge.NumPoints - 1);
				IsSimpleOp.AddEndpoint(dictionary, coordinate2, isClosed);
			}
			foreach (IsSimpleOp.EndpointInfo endpointInfo in dictionary.Values)
			{
				if (endpointInfo.IsClosed && endpointInfo.Degree != 2)
				{
					this._nonSimpleLocation = endpointInfo.Point;
					return true;
				}
			}
			return false;
		}

		private static void AddEndpoint(IDictionary<Coordinate, IsSimpleOp.EndpointInfo> endPoints, Coordinate p, bool isClosed)
		{
			IsSimpleOp.EndpointInfo endpointInfo;
			if (!endPoints.TryGetValue(p, out endpointInfo))
			{
				endpointInfo = new IsSimpleOp.EndpointInfo(p);
				endPoints.Add(p, endpointInfo);
			}
			endpointInfo.AddEndpoint(isClosed);
		}

		private readonly IGeometry _inputGeom;

		private readonly bool _isClosedEndpointsInInterior = true;

		private Coordinate _nonSimpleLocation;

		private class EndpointInfo
		{
			public EndpointInfo(Coordinate pt)
			{
				this.Point = pt;
			}

			public void AddEndpoint(bool isClosed)
			{
				this.Degree++;
				this.IsClosed = (this.IsClosed || isClosed);
			}

			public readonly Coordinate Point;

			public bool IsClosed;

			public int Degree;
		}
	}
}
